package com.courir;

import static com.courir.Predefined.listOf;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import lombok.RequiredArgsConstructor;
import lombok.val;

import org.joda.time.DateTime;

import com.courir.data.Altersgruppe;
import com.courir.data.CsvReader;
import com.courir.data.Lauf;
import com.google.common.base.Predicate;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;

/**
 * Stellt die VL-bezogenen Daten aus den Teilnehmern für die Reports bereit.
 * 
 * @author giese
 */
@RequiredArgsConstructor
public class VlData {
	/** Datum, an dem dieser VL stattfindet. */
	private final DateTime datum;

	/** CSV-Quelle */
	private final File source;
	/** Reader */
	private final CsvReader csv;

	/**
	 * Liest alle Teilnehmer mit diesen Daten aus.
	 */
	public List<XTeilnehmer> getTeilnehmer() throws IOException {
		val l = new ArrayList<XTeilnehmer>();
		for (val t : csv.read(source)) {
			l.add(new XTeilnehmer(datum, t));
		}
		return listOf(l);
	}

	public List<XTeilnehmer> getStartListe() throws IOException {
		val order = SortOrder.Lauf.compound(SortOrder.Name);
		return order.immutableSortedCopy(getTeilnehmer());
	}

	/**
	 * Liest die Liste aus, sortiert nach ergebnisklassen.
	 */
	public List<XTeilnehmer> getErgebnisListe() throws IOException {
		val order = SortOrder.Lauf.compound(SortOrder.Altersgruppe).compound(
				SortOrder.Zeit);
		val ts = order.immutableSortedCopy(Iterables.filter(getTeilnehmer(),
				SortOrder.HasResult));
		platzieren(ts);
		return ts;
	}

	/**
	 * Berechnet die Plätze der Teilnehmer, jeweils mit und ohne altersgruppen.
	 * 
	 * @param ts
	 */
	private void platzieren(List<XTeilnehmer> ts) {
		// laufplatzierungsindex erstellen
		val laufIndex = HashMultimap.<Lauf, XTeilnehmer> create();
		// Es werden nur die Teilnehmer berücksichtigt, die überhaupt eine Zeit
		// erreicht haben
		for (val t : Iterables.filter(ts, SortOrder.HasResult)) {
			laufIndex.put(t.getLauf(), t);
		}

		// 1. gesamtplatz im lauf
		for (val lts : laufIndex.asMap().values()) {
			// laufplatzierung
			int p = 1;
			for (val t : SortOrder.Zeit.sortedCopy(lts)) {
				t.setGesamtPlatz(p++);
			}
		}

		// 2. ergebnisklassen unterplatzierung!
		for (val lts : laufIndex.asMap().values()) {
			val agIndex = HashMultimap.<Altersgruppe, XTeilnehmer> create();
			for (val t : lts) {
				agIndex.put(t.getAltersgruppe(), t);
			}
			// agIndex erstellt
			for (val agTs : agIndex.asMap().values()) {
				// ag Platzierung
				int p = 1;
				for (val t : SortOrder.Zeit.sortedCopy(agTs)) {
					t.setPlatz(p++);
				}
			}
		}
	}
}

interface SortOrder {

	/** Liefert nur true für Teilnehmer die eine Zeit und einen Lauf haben. */
	Predicate<XTeilnehmer> HasResult = new Predicate<XTeilnehmer>() {
		public boolean apply(XTeilnehmer t) {
			return !t.getLauf().isEmpty() && t.getZeit().getMillis() > 0;
		}
	};

	Ordering<XTeilnehmer> Altersgruppe = new Ordering<XTeilnehmer>() {
		@Override
		public int compare(XTeilnehmer t1, XTeilnehmer t2) {
			return t1.getAltersgruppe().compareTo(t2.getAltersgruppe());
		}
	};

	Ordering<XTeilnehmer> Name = new Ordering<XTeilnehmer>() {
		@Override
		public int compare(XTeilnehmer t1, XTeilnehmer t2) {
			return t1.getName().compareTo(t2.getName());
		}
	};

	Ordering<XTeilnehmer> Lauf = new Ordering<XTeilnehmer>() {
		@Override
		public int compare(XTeilnehmer t1, XTeilnehmer t2) {
			return t1.getLauf().compareTo(t2.getLauf());
		}
	};

	Ordering<XTeilnehmer> Zeit = new Ordering<XTeilnehmer>() {
		@Override
		public int compare(XTeilnehmer t1, XTeilnehmer t2) {
			return t1.getZeit().compareTo(t2.getZeit());
		}
	};
}